
import { isDate, isPlainObject, isURLSearchParams } from './util';

// 保留这些特殊字符@、:、$、,、、[、]
function encode (val: string): string {
  return encodeURIComponent(val)
    .replace(/%40/g, '@')
    .replace(/%3A/gi, ':')
    .replace(/%24/g, '$')
    .replace(/%2C/gi, ',')
    .replace(/%20/g, '+')
    .replace(/%5B/gi, '[')
    .replace(/%5D/gi, ']')
}

/**
 * desc: 处理params参数，根据一些列规则，将参数拼接到url上(序列化params)
 * @param url    String  ajax url
 * @param params Object  ajax params
*/
export function buildURL(url: string, params?: any, paramsSerializer?: (params: any) => string ): string {
  if(!params) {
    return url;
  }
  let serializedParams;
  // 自定义参数解析规则
  if (paramsSerializer) {
    serializedParams = paramsSerializer(params)
  }else if(isURLSearchParams(params)){
    serializedParams = params.toString()
  }else {
      // 默认解析规则

      // 键值对数组：[key1=val1, key2=val2]
      const parts: string[] = [];
    
      // 1、遍历params，获取键值对， 将值统一转成数组, 并且忽略空值(/null/undefined)
      Object.keys(params).forEach(key => {
        let value = params[key];
        // 忽略空值
        if(value == null || typeof value === 'undefined') {
          return;
        }
        let values: any[] = []
        // 2、数组类型：key前面需要拼接[] /base/get?foo[]=bar&foo[]=baz'
        Array.isArray(value)? (values = value,  key += '[]') :  (values = [value])
    
        // 3、遍历values数组， 判断每个值的类型， 对val值做不同处理
        // 3-1、组装数据格式：[key1=val1， key2=val2], 每一个键值对都保存到数组中，key和value都需要通过encodeURIComponent转义，`@`、`:`、`$`、`,`、` `、`[`、`]`这几个不转义
        values.forEach(val => {
          if (isDate(val)) {
            val = val.toISOString()
          } else if (isPlainObject(val)) {
            val = JSON.stringify(val)
          }
          parts.push(`${encode(key)}=${encode(val)}`)
        })
      })
      // 将组装数据格式([key1=val1, key2=val2])通过&符号连接， 得到params序列化后的参数
      serializedParams = parts.join('&')
  }

  // 删除原url中#hash, 保留url上已有参数
  if(serializedParams) {
    const markIndex = url.indexOf('#')
    if(markIndex > -1) {
      serializedParams = serializedParams.slice(0, markIndex)
    }
    url += (url.indexOf("?") > -1? "&": '?') + serializedParams
  }
  return url;
}